package com.terrier.ui.managers
{
    import com.terrier.ui.core.WxComponent;
    import com.terrier.ui.core.wx_internal;
    import com.terrier.ui.effects.IWxAbstractEffect;
    import com.terrier.ui.effects.WxAbstractEffect;
    import com.terrier.ui.effects.WxAlphaEffect;
    import com.terrier.ui.effects.WxConcurrentEffect;
    import com.terrier.ui.effects.WxScaleEffect;
    import com.terrier.ui.events.WxSystemEvent;
    import com.terrier.ui.events.WxEffectEvent;
    import com.terrier.ui.events.WxMouseEvent;
    import com.terrier.utils.WxHashMap;

    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.geom.Rectangle;

    use namespace wx_internal;

    /**
     * 弹出窗口管理器
     *
     * @author Jarvis.weng
     */
    public class WxPopUpManager extends EventDispatcher
    {
        private static var s_instance:WxPopUpManager;

        private var _stage:Stage;

        private var _leftTop:Point = new Point(0, 0);

        private var _popUpMap:WxHashMap;

        public static function get instance():WxPopUpManager
        {
            if (s_instance == null)
            {
                s_instance = new WxPopUpManager(new Singleton());
            }
            return s_instance;
        }

        /**
         * 构造函数
         */
        public function WxPopUpManager(singleton:Singleton)
        {
            if (singleton == null)
            {
                throw new Error("不允许直接实例化该类型.");
            }
			
            _popUpMap = new WxHashMap();

            if (WxSystemManager.instance.stage)
            {
                initStage(WxSystemManager.instance.stage);
            }
            else
            {
                WxSystemManager.instance.addEventListener(WxSystemEvent.STAGE_INIT, onStageInit);
            }
        }

        public function removeAllPopUp():void
        {
            var popUpArray:Array = _popUpMap.keys;
            for (var i:int = 0; i < popUpArray.length; ++i)
            {
                var popUp:DisplayObject = popUpArray[i];
                removePopUp(popUp);
            }
        }

        /**
         *
         * @param reference
         * @param className
         * @param center
         * @param modal
         * @return
         */
        public function createPopUp(reference:DisplayObject, className:Class, center:Boolean = true, modal:Boolean = false):DisplayObject
        {
            var window:DisplayObject = new className();
            addPopUp(window, reference, center, modal);
            return window;
        }

        /**
         *
         * @param window
         * @param parent
         * @param center
         * @param modal
         */
        public function addPopUp(window:DisplayObject, reference:DisplayObject,
                                 center:Boolean = true, modal:Boolean = false, animate:Boolean = true,
                                 customEffect:WxAbstractEffect = null):void
        {

            if (_popUpMap.has(window))
            {
                bringToFront(window);
                return;
            }

            var info:PopUpItem = new PopUpItem(window, reference, modal, center);
            _popUpMap.add(window, info);
            WxSystemManager.instance.popupLayer.addChild(info);

            // 绘制模态窗口背景
            if (info.modal)
            {
                info.drawModal(_stage.stageWidth, _stage.stageHeight);
            }

            // 弹出窗口 中心对齐
            if (center)
            {
                centerPopUp(window);
            }

            if (animate)
            {
                var effect:IWxAbstractEffect = customEffect ? customEffect : getDefaultShowEffect();

                var ghost:Bitmap = new Bitmap();
                ghost.bitmapData = new BitmapData(window.width, window.height, true, 0x0);
                ghost.bitmapData.draw(window);
                ghost.smoothing = true;
                ghost.x = window.x;
                ghost.y = window.y;

                info.addChild(ghost);

                effect.target = ghost;
                effect.stopEventParams = { info: info, ghost: ghost };
                effect.addEventListener(WxEffectEvent.EFFECT_STOP, popUpShowEffectStopHandler);
                effect.start();
            }
            else
            {
                doPopUp(info);
            }
        }

        private function popUpShowEffectStopHandler(event:WxEffectEvent):void
        {
            var info:PopUpItem = PopUpItem(event.eventParams.info);
            var ghost:Bitmap = Bitmap(event.eventParams.ghost);
            doPopUp(info, ghost);
        }

        private function doPopUp(info:PopUpItem, ghost:Bitmap = null):void
        {
            info.addChild(DisplayObject(info.popUp));
            if (ghost != null && info.contains(ghost))
            {
                ghost.bitmapData.dispose();
                info.removeChild(ghost);
            }
        }

        private function getDefaultShowEffect():WxAbstractEffect
        {
            return new WxConcurrentEffect(
                new Array(
                new WxAlphaEffect(0.0, 1.0, 0.5) /*,
                new WxScaleEffect(new Point(0, 0), new Point(1.0, 1.0), 0.4)*/
                ),
                null);
        }

        private function getDefaultHideEffect():WxAbstractEffect
        {
            return new WxConcurrentEffect(
                new Array(
                new WxAlphaEffect(1.0, 0.5, 0.5) /*,
                new WxScaleEffect(new Point(1.0, 1.0), new Point(0.0, 0.0), 0.1, true)*/
                ),
                null);
        }

        /**
         *
         * @param popUp
         */
        public function centerPopUp(popUp:DisplayObject):void
        {
            if (_popUpMap.has(popUp))
            {
                var info:PopUpItem = _popUpMap.getValue(popUp);
                info.center = true;
                doCenterPopUp(info);
            }
        }

        /**
         *
         * @param popUp
         */
        public function removePopUp(popUp:DisplayObject,
                                    animate:Boolean = true,
                                    customEffect:WxAbstractEffect = null):void
        {
            if (_popUpMap.has(popUp))
            {
                var info:PopUpItem = _popUpMap.remove(popUp);

                if (animate)
                {
                    var effect:IWxAbstractEffect = customEffect ? customEffect : getDefaultHideEffect();

                    var window:DisplayObject = DisplayObject(info.popUp);

                    var ghost:Bitmap = new Bitmap();
                    ghost.bitmapData = new BitmapData(window.width, window.height, true, 0x0);
                    ghost.bitmapData.draw(window);
                    ghost.x = window.x;
                    ghost.y = window.y;

                    info.addChild(ghost);

                    if (info.contains(DisplayObject(window)))
                    {
                        info.removeChild(DisplayObject(window));
                        info.clearModal();
                    }

                    effect.target = ghost;
                    effect.stopEventParams = { info: info, bmp: ghost };
                    effect.addEventListener(WxEffectEvent.EFFECT_STOP, popUpHideEffectStopHandler);
                    effect.start();
                }
                else
                {
                    WxSystemManager.instance.popupLayer.removeChild(info);
                }
            }
        }

        private function popUpHideEffectStopHandler(event:WxEffectEvent):void
        {
            var bmp:Bitmap = Bitmap(event.eventParams.bmp);
            var info:PopUpItem = PopUpItem(event.eventParams.info);
            info.removeChild(bmp);
            bmp.bitmapData.dispose();
            WxSystemManager.instance.popupLayer.removeChild(info);
        }

        /**
         *
         * @param popUp
         */
        public function bringToFront(popUp:DisplayObject):void
        {
            if (_popUpMap.has(popUp))
            {
                var info:PopUpItem = _popUpMap.getValue(popUp);
                var index:int = WxSystemManager.instance.popupLayer.numChildren - 1;

                WxSystemManager.instance.popupLayer.setChildIndex(info, index);
            }
        }

        private function updatePopUp(info:PopUpItem):void
        {
            if (info.modal)
            {
                info.drawModal(_stage.stageWidth, _stage.stageHeight);
            }

            if (info.center)
            {
                doCenterPopUp(info);
            }
        }

        private function doCenterPopUp(info:PopUpItem):void
        {
            if (info.center)
            {
                var pos:Point = new Point(0, 0);
                if (info.reference && info.reference.parent)
                {
                    var bounds:Rectangle = info.reference.getBounds(info.reference);
                    pos.x = info.reference.x;
                    pos.y = info.reference.y;
                    pos = info.reference.parent.localToGlobal(pos);
                    pos.x += bounds.width / 2;
                    pos.y += bounds.height / 2;
                }
                else
                {
                    pos.x = _stage.stageWidth * 0.5;
                    pos.y = _stage.stageHeight * 0.5;
                }
                pos = info.globalToLocal(pos);

                var centerPos:Point;
                if (info.popUp is WxComponent)
                {
                    centerPos = (info.popUp as WxComponent).centerPoint;
                }
                else
                {
                    centerPos = new Point(info.popUp.width / 2, info.popUp.height / 2);
                }
                info.popUp.x = pos.x - centerPos.x;
                info.popUp.y = pos.y - centerPos.y;
            }
        }

        private function initStage(stage:Stage):void
        {
            _stage = stage;

            _leftTop.x = 0;
            _leftTop.y = 0;
            _leftTop = WxSystemManager.instance.popupLayer.globalToLocal(_leftTop);

            initEventHandler();
        }

        private function initEventHandler():void
        {
            _stage.addEventListener(MouseEvent.CLICK, onStageMouseClick);
            _stage.addEventListener(MouseEvent.MOUSE_DOWN, onStageMouseDown);
            _stage.addEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
            _stage.addEventListener(Event.RESIZE, onStageResize);
        }

        private function disposeEventHandler():void
        {
            _stage.removeEventListener(MouseEvent.CLICK, onStageMouseClick);
            _stage.removeEventListener(MouseEvent.MOUSE_DOWN, onStageMouseDown);
            _stage.removeEventListener(MouseEvent.MOUSE_UP, onStageMouseUp);
        }

        private function dispatchMouseEvent(type:String, e:MouseEvent):void
        {
            var index:int = WxSystemManager.instance.popupLayer.numChildren - 1;

            if (index >= 0)
            {
                var info:PopUpItem = WxSystemManager.instance.popupLayer.getChildAt(index) as PopUpItem;
                var popUp:DisplayObject = info.popUp;

                var offset:Point = new Point(0, 0);
                offset = popUp.globalToLocal(offset);

                if (!popUp.hitTestPoint(e.stageX, e.stageY, true))
                {
                    var newEvent:WxMouseEvent = new WxMouseEvent(type, e.bubbles, e.cancelable,
                                                                 e.localX, e.localY,
                                                                 e.relatedObject,
                                                                 e.ctrlKey, e.altKey, e.shiftKey,
                                                                 e.buttonDown,
                                                                 e.delta);
                    popUp.dispatchEvent(newEvent);
                }
            }
        }

        //--------------------------------------------------------
        //
        // Event Handlers
        //
        //--------------------------------------------------------

        private function onStageMouseDown(e:MouseEvent):void
        {
            dispatchMouseEvent(WxMouseEvent.MOUSE_DOWN_OUTSIDE, e);
        }

        private function onStageMouseUp(e:MouseEvent):void
        {
            dispatchMouseEvent(WxMouseEvent.MOUSE_UP_OUTSIDE, e);
        }

        private function onStageMouseClick(e:MouseEvent):void
        {
            dispatchMouseEvent(WxMouseEvent.CLICK_OUTSIDE, e);
        }

        private function onStageMouseWheel(e:MouseEvent):void
        {
            dispatchMouseEvent(WxMouseEvent.MOUSE_WHEEL_OUTSIDE, e);
        }

        private function onStageResize(e:Event):void
        {
            // 更新所有模态窗口背景
            _popUpMap.eachValue(updatePopUp);
        }

        private function onStageInit(e:WxSystemEvent):void
        {
            initStage(e.stage);

            // 更新所有模态窗口背景
            _popUpMap.eachValue(updatePopUp);
        }
    }
}

import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.display.Stage;
import flash.geom.Point;

class PopUpItem extends Sprite
{
    private var _popUp:DisplayObject;

    private var _reference:DisplayObject;

    private var _center:Boolean;

    private var _modal:Boolean;

    public function PopUpItem(popUp:DisplayObject, reference:DisplayObject, modal:Boolean, center:Boolean)
    {
        _popUp = popUp;
        _reference = reference;
        _center = center;
        _modal = modal;
    }

    //------------------------------------------------
    //
    // Methods
    //
    //------------------------------------------------
    public function drawModal(w:Number, h:Number):void
    {
        graphics.clear();
        graphics.beginFill(0x0, 0.5);
        graphics.drawRect(0, 0, w, h);
        graphics.endFill();
    }

    public function clearModal():void
    {
        graphics.clear();
    }

    //------------------------------------------------
    //
    // Properties
    //
    //------------------------------------------------

    public function get popUp():DisplayObject
    {
        return _popUp;
    }

    public function set popUp(value:DisplayObject):void
    {
        _popUp = value;
    }

    public function get reference():DisplayObject
    {
        return _reference;
    }

    public function set reference(value:DisplayObject):void
    {
        _reference = value;
    }

    public function get center():Boolean
    {
        return _center;
    }

    public function set center(value:Boolean):void
    {
        _center = value;
    }

    public function get modal():Boolean
    {
        return _modal;
    }

    public function set modal(value:Boolean):void
    {
        _modal = value;
    }

}

class Singleton
{
	
}